RDB から選択型を読み取る
実例
ContactId を用いて、単一の ContactInfo を取得する readOneContact 関数の実装
code:fsharp
type Contact =
{ ContactId: ContactId
Info: ContactInfo }
and ContactInfo =
| Email of EmailAddress
| Phone of PhoneNumber
and EmailAddress = EmailAddress of string
and PhoneNumber = PhoneNumber of string
and ContactId = ContactId of int
DB には、すべてのケースを同じテーブルに格納している
1. クエリの型を定義する
code:fsharp
type ReadOneContact = SqlCommandProvider<"""
SELECT ContactId, IsEmail, IsPhone, EmailAddress, PhoneNumber
FROM ContactInfo
WHERE ContactId = @contactId
""", connectionString>
2. toDomain の実装
フラグをチェックして ContactInfo のケースを決定し、子の result 式を使って各ケースのデータを組み立てる
code:fsharp
let toDomain (dbRecord: ReadOneContact.Record): Result<Contact,_> =
result {
let! contactId =
dbRecord.ContactId
|> ContactId.create
let! contactInfo =
if dbRecord.IsEmail then
result {
let! emailAddressString =
dbRecord.EmailAddress
|> Result.ofOption "Email expected to be non null"
let! emailAddress =
emailAddressString |> EmailAddress.create
return (Email emailAddress)
}
else
result {
let! phoneNumberString =
dbRecord.PhoneNumber
|> Result.ofOption "PhoneNumber expected to be non null"
let! phoneNumber =
phoneNumberString |> PhoneNumber.create
return (Phone phoneNumber)
}
let contact =
{ ContactId = contactId
Info = contactInfo }
return contact
}
Result.ofOption は Option を Result に変換するヘルパ関数である
code:fsharp
module Result =
let ofOption errorValue opt =
match opt with
| Some v -> Ok v
| None -> Error errorValue
3. readOneContact の実装
convertSingleDbRecord を用いると、以下のように簡潔に書ける
code:fsharp
let readOneContact (productionConnection: SqlConnection) (ContactId contactId) =
use cmd = new ReadOneContact(productionConnection)
let tableName = "ContactInfo"
let records = cmd.Execute(contactId = contactId) |> Seq.toList
convertSingleDbRecord tableName contactId records toDomain
toDomain が複雑で ORM が使いたくなる しかし、メールアドレスや注文数のチェック、ネストした選択型の処理が出来ないため、ドメイン の 整合性 を担保できないため使わないほうが良い